package cn.keniykey.mc.landterritory.land;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.UUID;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.scheduler.BukkitRunnable;

import cn.keniykey.mc.landterritory.LandTerritoryMain;
import cn.keniykey.mc.landterritory.config.ConfigurationManager;
import cn.keniykey.mc.landterritory.config.LanguageManager;
import cn.keniykey.mc.landterritory.land.api.LandPoint;
import cn.keniykey.mc.landterritory.land.api.LandRange;
import cn.keniykey.mc.landterritory.land.api.LandUtil;
import cn.keniykey.mc.landterritory.vault.Perm;

public final class RangeShowManager implements Listener{
	private static HashMap<UUID,LandRange> hashmap_shown_ranges = new HashMap<>();
	private static HashMap<UUID,Long> hashmap_show_countdown = new HashMap<>();
	
	@EventHandler(priority = EventPriority.LOWEST)
	public void onPlayerQuit(final PlayerQuitEvent e) {
		final UUID player_uuid = e.getPlayer().getUniqueId();
		hashmap_shown_ranges.remove(player_uuid);
		hashmap_show_countdown.remove(player_uuid);
	}
	@EventHandler(priority = EventPriority.MONITOR,ignoreCancelled = true)
	public void onPlayerTeleport(final PlayerTeleportEvent e) {
		final Player player = e.getPlayer();
		if(!hashmap_shown_ranges.containsKey(player.getUniqueId())) return;
		if(player.getLocation().getWorld().getName().equals(hashmap_shown_ranges.get(player.getUniqueId()).getWorldName())) return;
		hashmap_shown_ranges.remove(player.getUniqueId());
	}
	
	public static void cancelLastShown(Player player) {
		final UUID player_uuid = player.getUniqueId();
		if(!hashmap_shown_ranges.containsKey(player_uuid)) return;
		final LandRange range = hashmap_shown_ranges.remove(player_uuid);
		final Location loc_player = player.getLocation();
		if(!range.getWorldName().equals(loc_player.getWorld().getName())) return;
		if((LandUtil.getMinDistanceXZ(loc_player, range) / 16) > Bukkit.getViewDistance()) return;
		Bukkit.getScheduler().runTaskAsynchronously(LandTerritoryMain.getPlugin(), new ShowRangeInitRunnable(player, range, false));
	}
	private static void showRange(Player player,LandRange range) {
		int second  = 1;
		if(hashmap_shown_ranges.containsKey(player.getUniqueId())) {
			cancelLastShown(player);
			second = (ConfigurationManager.View_LandInfo_Show_Second *
					(ConfigurationManager.View_LandInfo_Show_AmountLimit / ConfigurationManager.View_LandInfo_Show_SecondAmount));
		}
		final Location loc_player = player.getLocation();
		if(!loc_player.getWorld().getName().equals(range.getWorldName())) return;
		Bukkit.getScheduler().runTaskLaterAsynchronously(LandTerritoryMain.getPlugin(),
				new ShowRangeInitRunnable(player, range, true), (second*20));
	}
	/**
	 * @param judge 是否启用判断
	 */
	public static void showRange(Player player,LandRange range,boolean judge) {
		if(!range.isRangeAllow()) return;
		range.fit();
		if(!judge) {
			showRange(player, range);
			return;
		}
		if(!range.getWorldName().equals(player.getLocation().getWorld().getName())) {
			player.sendMessage(LanguageManager.getChatLang(1104153));
			return;
		}
		if(!Perm.getPerm().hasPermission(range.getWorldName(), player, ConfigurationManager.View_LandInfo_Show_Per)) {
			player.sendMessage(LanguageManager.getChatLang(1104150));
			return;
		}
		final UUID player_uuid = player.getUniqueId();
		if(ConfigurationManager.View_LandInfo_Show_Countdown > 0 && !player.isOp()) {
			final long nowtime = System.currentTimeMillis();
			if(hashmap_show_countdown.containsKey(player_uuid)) {
				final long millis = (nowtime - hashmap_show_countdown.get(player_uuid));
				if(millis < (ConfigurationManager.View_LandInfo_Transfer_Countdown * 1000)) {
					player.sendMessage(LanguageManager.getChatLang(1104151, (millis / 1000), (millis % 1000)));
					return;
				}
			}else {
				hashmap_show_countdown.put(player_uuid, nowtime);
			}
		}
		showRange(player, range);
		player.sendMessage(LanguageManager.getChatLang(1104152));
	}
	private final static class ShowRangeInitRunnable implements Runnable {
		private final Player player1;
		private final LandRange range1;
		private final boolean show1;
		private LinkedList<Location> linked_showlocs1;
		ShowRangeInitRunnable(Player pl,LandRange range,boolean show) {
			this.player1 = pl;
			this.range1 = range;
			this.show1 = show;
		}
		@Override
		public void run() {
			final long length = this.range1.getTotalLength();
			final LandPoint point1 = this.range1.getLandPointOne(),point2 = this.range1.getLandPointTwo();
	        final int x1 = point1.getX(),x2 = point2.getX();
	        final int y1 = point1.getY(),y2 = point2.getY();
	        final int z1 = point1.getZ(),z2 = point2.getZ();
	        final int maxblock = ConfigurationManager.View_LandInfo_Show_AmountLimit;
	        long interval = 1;
	        if (length > maxblock) {
	            interval = length / maxblock;
	            if (length % maxblock != 0) interval++;
	        }
	        final World w = Bukkit.getServer().getWorld(this.range1.getWorldName());
	        this.linked_showlocs1 = new LinkedList<>();
	        addX(w, x1, x2, y1, z1, interval);
	        addX(w, x1, x2, y2, z1, interval);
	        addX(w, x1, x2, y1, z2, interval);
	        addX(w, x1, x2, y2, z2, interval);
	        addY(w, x1, y1, y2, z1, interval);
	        addY(w, x2, y1, y2, z1, interval);
	        addY(w, x1, y1, y2, z2, interval);
	        addY(w, x2, y1, y2, z2, interval);
	        addZ(w, x1, y1, z1, z2, interval);
	        addZ(w, x2, y1, z1, z2, interval);
	        addZ(w, x1, y2, z1, z2, interval);
	        addZ(w, x2, y2, z1, z2, interval);
	        add(w, x1, y1, z1);
	        add(w, x2, y1, z1);
	        add(w, x1, y2, z1);
	        add(w, x1, y1, z2);
	        add(w, x2, y2, z1);
	        add(w, x1, y2, z2);
	        add(w, x2, y1, z2);
	        add(w, x2, y2, z2);
	        if(!this.linked_showlocs1.isEmpty()) {
	        	new ShowRangeBukkitRunnable(this.player1, this.show1, this.linked_showlocs1).runTaskTimerAsynchronously(
	        			LandTerritoryMain.getPlugin(), 20l, (ConfigurationManager.View_LandInfo_Show_Second * 20));
	        }
		}
		private void add(final World w, final int x, final int y, final int z) {
			this.linked_showlocs1.add(new Location(w, x, y, z));
		}
		private void addX(final World w, final int x1, final int x2,final int y, final int z, final long interval) {
			for (int x = x1; x <= x2; x += interval) this.add(w, x, y, z);
		}
		private void addZ(final World w, final int x,final int y, final int z1,final int z2, final long interval) {
			for (int z = z1; z <= z2; z += interval) this.add(w, x, y, z);
		}
		private void addY(final World w, final int x,final int y1,final int y2, final int z, final long interval) {
			for (int y = y1; y <= y2; y += interval) this.add(w, x, y, z);
		}
	}
	private static final class ShowRangeBukkitRunnable extends BukkitRunnable {
		private final Player player1;
		private final boolean show1;
		private final LinkedList<Location> linked_showlocs1;
		private final String worldname1;
		
		ShowRangeBukkitRunnable(Player player,boolean show,LinkedList<Location> linked_showlocs) {
			this.player1 = player;
			this.show1 = show;
			this.linked_showlocs1 = linked_showlocs;
			this.worldname1 = linked_showlocs.getFirst().getWorld().getName();
		}
		
		@Override
		public void run() {
			if(!this.player1.isOnline()) {
				hashmap_shown_ranges.remove(this.player1.getUniqueId());
				this.linked_showlocs1.clear();
				this.cancel();
				return;
			}
			final Location loc_player = this.player1.getLocation();
			if(!loc_player.getWorld().getName().equals(this.worldname1)) {
				hashmap_shown_ranges.remove(this.player1.getUniqueId());
				this.linked_showlocs1.clear();
				this.cancel();
				return;
			}
			final int x_player = loc_player.getBlockX(),z_player = loc_player.getBlockZ();
			int sent_amount = 0;
			while(!this.linked_showlocs1.isEmpty() && sent_amount < ConfigurationManager.View_LandInfo_Show_SecondAmount) {
				final Location loc_show = this.linked_showlocs1.removeFirst();
				final int distance = Math.min(Math.abs((x_player - loc_show.getBlockX())), Math.abs((z_player - loc_show.getBlockZ())));
				if((distance / 16) > Bukkit.getViewDistance()) continue;
				if(this.show1) {
					this.player1.sendBlockChange(loc_show, ConfigurationManager.View_LandInfo_Show_Block.createBlockData());
				}else {
					final Block block = loc_show.getBlock();
//					this.player1.sendBlockChange(loc, block.getTypeId(), block.getData());
					this.player1.sendBlockChange(loc_show, block.getBlockData());
				}
				sent_amount++;
			}
			if(this.linked_showlocs1.isEmpty()) this.cancel();
		}
		
	}
}